package asteroids.model.programs;

import java.util.HashMap;
import java.util.List;

import asteroids.model.programs.Expression.Addition;
import asteroids.model.programs.Expression.BooleanEquality;
import asteroids.model.programs.Expression.BooleanInequality;
import asteroids.model.programs.Expression.BooleanLiteral;
import asteroids.model.programs.Expression.Conjunction;
import asteroids.model.programs.Expression.Cosine;
import asteroids.model.programs.Expression.Disjunction;
import asteroids.model.programs.Expression.Division;
import asteroids.model.programs.Expression.DoubleEquality;
import asteroids.model.programs.Expression.DoubleInequality;
import asteroids.model.programs.Expression.DoubleLiteral;
import asteroids.model.programs.Expression.EntityEquality;
import asteroids.model.programs.Expression.EntityInequality;
import asteroids.model.programs.Expression.Expression;
import asteroids.model.programs.Expression.GetDirectionMethod;
import asteroids.model.programs.Expression.GetRadiusMethod;
import asteroids.model.programs.Expression.GetVXMethod;
import asteroids.model.programs.Expression.GetVYMethod;
import asteroids.model.programs.Expression.GetXMethod;
import asteroids.model.programs.Expression.GetYMethod;
import asteroids.model.programs.Expression.GreaterThan;
import asteroids.model.programs.Expression.GreaterThanOrEqualTo;
import asteroids.model.programs.Expression.LessThan;
import asteroids.model.programs.Expression.LessThanOrEqualTo;
import asteroids.model.programs.Expression.Multiplication;
import asteroids.model.programs.Expression.Negation;
import asteroids.model.programs.Expression.NullLiteral;
import asteroids.model.programs.Expression.SelfLiteral;
import asteroids.model.programs.Expression.Sine;
import asteroids.model.programs.Expression.SquareRoot;
import asteroids.model.programs.Expression.Substraction;
import asteroids.model.programs.Expression.VariableEquality;
import asteroids.model.programs.Expression.VariableInequality;
import asteroids.model.programs.Expression.VariableLiteral;
import asteroids.model.programs.Statement.AssignmentLiteral;
import asteroids.model.programs.Statement.FireAction;
import asteroids.model.programs.Statement.ForEachLiteral;
import asteroids.model.programs.Statement.IfThenElseLiteral;
import asteroids.model.programs.Statement.PrintLiteral;
import asteroids.model.programs.Statement.SequenceLiteral;
import asteroids.model.programs.Statement.SkipAction;
import asteroids.model.programs.Statement.Statement;
import asteroids.model.programs.Statement.ThrustAction;
import asteroids.model.programs.Statement.ThrustOffAction;
import asteroids.model.programs.Statement.TurnAction;
import asteroids.model.programs.Statement.WhileLiteral;
/**
* 
* @version 0.3
* @author  Tandan Sunil, Van Deun Luca
*
*/
public class ProgramFactoryImpl  implements asteroids.model.programs.parsing.ProgramFactory<Expression, Statement, Type>{

	@Override
	public Expression createDoubleLiteral(int line, int column, double d) {
		return new DoubleLiteral(line, column, d);
	}

	@Override
	public Expression createBooleanLiteral(int line, int column, boolean b) {
		
		return new BooleanLiteral(line, column, b);
	}

	@Override
	public Expression createAnd(int line, int column, Expression e1,
			Expression e2) {
		return new Conjunction(line, column, e1, e2);
	}

	@Override
	public Expression createOr(int line, int column, Expression e1,
			Expression e2) {
		return new Disjunction(line, column, e1, e2);
	}

	@Override
	public Expression createNot(int line, int column, Expression e) {
		return new Negation(line, column, e);
	}

	@Override
	public Expression createNull(int line, int column) {
		return new NullLiteral(line, column);
	}

	@Override
	public Expression createSelf(int line, int column) {
		if(selfLiteral == null)
			selfLiteral = new SelfLiteral(line,column);
		return selfLiteral;
	}
	private SelfLiteral selfLiteral;
	@Override
	public Expression createGetX(int line, int column, Expression e) {
		return new GetXMethod(line, column, e);
	}

	@Override
	public Expression createGetY(int line, int column, Expression e) {
		return new GetYMethod(line, column, e);
	}

	@Override
	public Expression createGetVX(int line, int column, Expression e) {
		return new GetVXMethod(line, column, e);
	}

	@Override
	public Expression createGetVY(int line, int column, Expression e) {
		return new GetVYMethod(line, column, e);
	}

	@Override
	public Expression createGetRadius(int line, int column, Expression e) {
		return new GetRadiusMethod(line, column, e);
	}

	@Override
	public Expression createVariable(int line, int column, String name) {
		if(variables.get(name)!=null)
			return variables.get(name);
		VariableLiteral toreturn = new VariableLiteral(line, column, name);
		variables.put(name, toreturn);
		return toreturn;
	}
	private HashMap<String,VariableLiteral> variables = new HashMap<String,VariableLiteral>();

	public HashMap<String,VariableLiteral> getVariables(){
		return variables;
	}
	@Override
	public Expression createLessThan(int line, int column, Expression e1,
			Expression e2) {
		return new LessThan(line, column, e1, e2);
	}

	@Override
	public Expression createGreaterThan(int line, int column, Expression e1,
			Expression e2) {
		return new GreaterThan(line, column, e1, e2);
	}

	@Override
	public Expression createLessThanOrEqualTo(int line, int column,
			Expression e1, Expression e2) {
		return new LessThanOrEqualTo(line, column, e1, e2);
	}

	@Override
	public Expression createGreaterThanOrEqualTo(int line, int column,
			Expression e1, Expression e2) {
		return new GreaterThanOrEqualTo(line, column, e1, e2);
	}

	@Override
	public Expression createEquality(int line, int column, Expression e1,
			Expression e2) {
		
		if(e1 instanceof NullLiteral|| e2 instanceof NullLiteral){
			return new EntityEquality(line, column, e1, e2);
		}
		if(e1.getType() != null && e2.getType() != null){
			if(e1.getType().getType()==Type.AllType.BOOLEAN)
				return new BooleanEquality(line, column, e1, e2);
			if(e1.getType().getType()==Type.AllType.DOUBLE)
				return new DoubleEquality(line, column, e1, e2);
			if(e1.getType().getType()==Type.AllType.ENTITY)
				return new EntityEquality(line, column, e1, e2);
		}
		return new VariableEquality(line, column, e1, e2);
	}

	@Override
	public Expression createInequality(int line, int column, Expression e1,
			Expression e2) {
		if(e1 instanceof NullLiteral|| e2 instanceof NullLiteral){
			return new EntityInequality(line, column, e1, e2);
		}
		if(e1.getType() != null && e2.getType() != null){
			if(e1.getType().getType()==Type.AllType.BOOLEAN)
				return new BooleanInequality(line, column, e1, e2);
			if(e1.getType().getType()==Type.AllType.DOUBLE)
				return new DoubleInequality(line, column, e1, e2);
			if(e1.getType().getType()==Type.AllType.ENTITY)
				return new EntityInequality(line, column, e1, e2);
		}
		return new VariableInequality(line, column, e1, e2);
	}

	@Override
	public Expression createAdd(int line, int column, Expression e1,
			Expression e2) {
		return new Addition(line, column, e1, e2);
	}

	@Override
	public Expression createSubtraction(int line, int column, Expression e1,
			Expression e2) {
		return new Substraction(line, column, e1, e2);
	}

	@Override
	public Expression createMul(int line, int column, Expression e1,
			Expression e2) {
		return new Multiplication(line, column, e1, e2);
	}

	@Override
	public Expression createDivision(int line, int column, Expression e1,
			Expression e2) {
		return new Division(line, column, e1, e2);
	}

	@Override
	public Expression createSqrt(int line, int column, Expression e) {
		return new SquareRoot(line, column, e);
	}

	@Override
	public Expression createGetDirection(int line, int column) {
		GetDirectionMethod exp = new GetDirectionMethod(line, column);
		exp.setSelfLiteral((SelfLiteral) (createSelf(line, column)));
		return exp;
	}

	@Override
	public Expression createSin(int line, int column, Expression e) {
		return new Sine(line, column, e);
	}

	@Override
	public Expression createCos(int line, int column, Expression e) {
		return new Cosine(line, column, e);
	}

	@Override
	public Statement createEnableThruster(int line, int column) {
		return new ThrustAction(line, column);
	}

	@Override
	public Statement createDisableThruster(int line, int column) {
		return new ThrustOffAction(line, column);
	}

	@Override
	public Statement createFire(int line, int column) {
		return new FireAction(line, column);
	}

	@Override
	public Statement createTurn(int line, int column, Expression angle) {
		return new TurnAction(line, column, angle);
	}

	@Override
	public Statement createAssignment(int line, int column, String variable,
			Expression rhs) {
		return new AssignmentLiteral(line,column,variable,rhs);
	}

	@Override
	public Statement createIf(int line, int column, Expression condition,
			Statement then, Statement otherwise) {
		return new IfThenElseLiteral(line, column, condition, then, otherwise);
	}

	@Override
	public Statement createWhile(int line, int column, Expression condition,
			Statement body) {
		return new WhileLiteral(line, column, condition, body);
	}

	@Override
	public Statement createForeach(int line, int column,
			asteroids.model.programs.parsing.ProgramFactory.ForeachType type,
			String variableName, Statement body) {
		return new ForEachLiteral(line, column, type, variableName, body);
	}

	@Override
	public Statement createSkip(int line, int column) {
		return new SkipAction(line, column);
	}

	@Override
	public Statement createSequence(int line, int column,
			List<Statement> statements) {
		return new SequenceLiteral(line, column, statements);
	}

	@Override
	public Statement createPrint(int line, int column, Expression e) {
		return new PrintLiteral(line, column, e);
	}

	@Override
	public Type createDoubleType() {

		return new Type(Type.AllType.DOUBLE);
	}

	@Override
	public Type createBooleanType() {
		return new Type(Type.AllType.BOOLEAN);
	}

	@Override
	public Type createEntityType() {
		return new Type(Type.AllType.ENTITY);
	}

	}
